home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #18 (Mar 87) / Printer MPW sources / PrintTest.p < prev    next >
Text File  |  1986-11-30  |  30KB  |  983 lines

  1. (* @(#) PrintTest.p 11/30/86
  2.     {Sources}PrDrvr:PrintTest.p
  3.  
  4.     Examine and display TPrint record values
  5.     Written by Joel West, Western Software Technology
  6.  
  7.     MPW Pascal source
  8.  
  9.     Begun with an underlying skeleton, using part of an example from
  10.     Apple User Education
  11.     File:    Example code for a text editor
  12.         by Cary Clark, Macintosh Technical Support
  13.         Version 1.1    May 13, 1985
  14.  
  15.     Portions copyright   © 1986 Joel West, Western Software Technology
  16. *)
  17.  
  18. PROGRAM PrintTest;
  19.  
  20. {$R-} {Turn off range checking.}
  21. {$D+} {Debugging symbols.}
  22.  
  23.  
  24. {     A document in this program (built using TextEdit) is used to
  25.     display the contents of a modified TPrint record, and for no
  26.     other purpose.
  27.  
  28.     There are Job and Style dialogs for modifying the record, and
  29.     for actually printing out the document.
  30. }
  31.  
  32.   USES
  33.     MemTypes,
  34.     QuickDraw,
  35.     OSIntf,
  36.     ToolIntf,
  37.     PackIntf,
  38.     MacPrint,
  39.     StringFormat;        { own print formatting routines }
  40.  
  41.   TYPE
  42.     WordPtr = ^INTEGER;
  43.     MyWindMode = (NullMode, OpenMode, DAMode);
  44.     WindowData = RECORD        { Can only hold one 32-bit value in wRefCon }
  45.       theTE: TEHandle;        { for TextEdit record }
  46.       theTHP: THPrint;        { the TPrint we are analyzing }
  47.     END;
  48.     WindowDataPtr = ^WindowData;
  49.     WindowDataHandle = ^WindowDataPtr;
  50.  
  51.   CONST
  52. { Change these to suit your taste }
  53.     myStdFont = monaco;
  54.     myStdSize = 9;
  55.     myHdgFont = systemFont;    { aka Chicago }
  56.     myHdgSize = 12;
  57. { menus }
  58.     appleMenu = 1;
  59.     fileMenu = 2;
  60.         newItem = 1;
  61.     closeItem = 2;
  62.     stlItem = 4;
  63.     jobItem = 5;
  64.     setupItem = 7;
  65.     printItem = 8;
  66.     quitItem = 10;
  67.       lastFileItem = 10;
  68.     editMenu = 3;
  69.     lastMenu = 3; {Number of menus}
  70.  
  71. { Resources }
  72.     ALRT_about    = 256;        { About… message }
  73.     ALRT_printerr = 257;    { report printing error }
  74.     DLOG_printing = 258;    { printing status dialog }
  75.     STR_id    = 0;
  76.     STR_pagehead= 256;        { page heading }
  77.     STR_prepare    = 300;        { messages for printing status }
  78.     STR_printing= 301;
  79.     STR_spooling= 302;
  80.     STR_of    = 303;
  81.     STR_prspool    = 304;
  82.     STRN_scan    = 256;        { enumeration literals }
  83.     STRN_feed    = 257;
  84.     STRN_wdev    = 258;
  85.     STRN_job    = 259;
  86.     STRN_bool    = 260;
  87.     WIND_main    = 256;
  88.  
  89. {Constant declared for field windowKind}
  90.     myDocument = 8;
  91.  
  92. { Character }
  93.     Return = $0D;
  94.  
  95.   VAR
  96. {The first six variables are changed as windows are activated}
  97.     myWindow: WindowPtr;
  98.     myPeek: WindowPeek;
  99.     hTE: TEHandle; {The active text edit handle}
  100.     printHdl: THPrint; {for actual printing}
  101.     myMenus: ARRAY [1..lastMenu] OF MenuHandle;
  102.     dragRect: Rect;
  103.     theChar: CHAR; {Keyboard input goes here}
  104.     doneFlag: BOOLEAN;
  105.     printFlag: BOOLEAN; {The user selected 'Print…' from the File menu}
  106.     currWMode: MyWindMode;    { sets menu options }
  107.     myEvent: EventRecord; {Shared by all routines}
  108.     watchHdl: CursHandle; {The wait cursor}
  109.     wdh: WindowDataHandle;    { temporary }
  110.     ph: THPrint;        { temporary for analyzed TPrint record }
  111.     spare: Ptr; {a preallocated area to be used by the next window}
  112.     linebuff: Str255;
  113.  
  114.   PROCEDURE MainEventLoop;
  115.     FORWARD;
  116.  
  117. { ============================TPrint formatting Segment========================}
  118. {$S DumpTPrint }
  119.  
  120. {---------------------------------Add a line to document-----------------------}
  121.   PROCEDURE PrintLine;
  122.  
  123.     VAR
  124.       p: Ptr;
  125.       c: SignedByte;
  126.  
  127.     BEGIN        {this adds the line to end of the display}
  128.       p := @linebuff;
  129.       TEInsert(Pointer(ORD4(p)+1), Length(linebuff), hTE);
  130.       c := Return;
  131.       TEInsert(@c, 1, hTE);    { add CR }
  132.     WITH hTE^^ DO        { Check to see if we're beyond bottom of page }
  133.       IF (lineHeight*nLines) > (viewRect.bottom - viewRect.top)  THEN
  134.           TEScroll (0, -hTE^^.lineHeight, hTE);    { scroll up one line }
  135.         linebuff := '';
  136.     END; {PrintLine}
  137.  
  138.  
  139. {-------------------------------Formatting utilities---------------------------}
  140.     PROCEDURE PrintTab;
  141.     VAR
  142.         col, nexttab: INTEGER;
  143.     BEGIN        { add spaces to next multiple of 8 }
  144.         col := Length(linebuff);
  145.         nexttab := col - BAND(col,7) + 8 ;
  146.         linebuff[0] := CHR(nexttab);
  147.         WHILE col < nexttab DO
  148.           BEGIN
  149.         col := col+1;
  150.         linebuff[col] := ' ';
  151.           END;
  152.     END;
  153.     PROCEDURE PrintHex(n: LONGINT; w: INTEGER);
  154.     BEGIN
  155.         SWriteHex(linebuff, n, w);    { actual width }
  156.     END;
  157.     PROCEDURE PrintInt(n: LONGINT);
  158.     BEGIN
  159.         SWriteInt(linebuff, n, 0);    { minimum width }
  160.     END;
  161.     PROCEDURE PrintString(s: Str255);
  162.     BEGIN
  163.         SWriteString(linebuff, s);
  164.     END;
  165.     PROCEDURE PrintStrNum(s: Str255; n: LONGINT);
  166.     BEGIN                { format a string and integer }
  167.         PrintTab;
  168.         SWriteString(linebuff, s);
  169.         SWriteInt(linebuff, n, 0);    { minimum width }
  170.     END;
  171.     PROCEDURE PrintStrHex(s: Str255; n: LONGINT; w: INTEGER);
  172.     BEGIN                { format a string and hex }
  173.         PrintTab;
  174.         SWriteString(linebuff, s);
  175.         SWriteHex(linebuff, n, w);
  176.     END;
  177.  
  178.  
  179. { --------------------------- Format enumeration ----------------------------- }
  180.     PROCEDURE DumpEnum(msg: Str255; val, resid: INTEGER);
  181.     VAR
  182.         s: Str255;
  183.         rh: Handle;
  184.         limitp: WordPtr;
  185.     BEGIN
  186.         PrintTab;
  187.         PrintString(msg);
  188.         rh := GetResource('STR#', resid);
  189.         IF (rh <> NIL) THEN        { if we screwed up, don't try to format }
  190.             BEGIN
  191.         limitp := WordPtr(rh^);    { number of strings }
  192.         IF (val >= 0) AND (val < limitp^) THEN
  193.             BEGIN        { in range defined }
  194.             GetIndString(s, resid, val+1);
  195.             PrintString(s);
  196.             EXIT(DumpEnum);
  197.             END
  198.         END;
  199.         PrintInt(val);        { no string, show the integer }
  200.     END;
  201.  
  202. { -------------------------------- Format Rect ------------------------------- }
  203.     PROCEDURE DumpRect(msg: Str255; r: Rect);
  204.     BEGIN
  205.         PrintString(msg);
  206.         PrintString(': {');
  207.         PrintInt(r.top);
  208.         PrintString(', ');
  209.         PrintInt(r.left);
  210.         PrintString(', ');
  211.         PrintInt(r.bottom);
  212.         PrintString(', ');
  213.         PrintInt(r.right);
  214.         PrintString('}');
  215.         PrintLine;
  216.     END;
  217.  
  218. { -------------------------------- Format TPrInfo ---------------------------- }
  219.     PROCEDURE DumpPrInfo(msg: Str255; prinf: TPrInfo);
  220.     BEGIN
  221.         PrintString(msg);
  222.         PrintStrNum('iDev: ', prinf.iDev);
  223.         PrintStrNum('iVRes: ', prinf.iVRes);
  224.         PrintStrNum('iHRes: ', prinf.iHRes);
  225.         PrintLine;
  226.         DumpRect('rPage', prinf.rPage);
  227.     END;
  228.  
  229. { -------------------------------- Format TPrXInfo --------------------------- }
  230.     PROCEDURE DumpPrXInfo(msg: Str255; prxi: TPrXInfo);
  231.     BEGIN
  232.         PrintString(msg);
  233.         PrintStrNum('iRowBytes: ', prxi.iRowBytes);
  234.         PrintStrNum('iBandH: ', prxi.iBandV);
  235.         PrintStrNum('iBandV: ', prxi.iBandH);
  236.         PrintLine;
  237.         PrintStrNum('iDevBytes: ', prxi.iDevBytes);
  238.         PrintStrNum('iBands: ', prxi.iBands);
  239.         PrintLine;
  240.         PrintStrNum('bPatScale: ', prxi.bPatScale);
  241.         PrintStrNum('bUlThick: ', prxi.bUlThick);
  242.         PrintStrNum('bUlOffset: ', prxi.bUlOffset);
  243.         PrintStrNum('bUlShadow: ', prxi.bUlShadow);
  244.         PrintLine;
  245.  
  246.         DumpEnum('scan: ', ORD(prxi.scan), STRN_scan);
  247.  
  248.         PrintStrNum('bXInfoX: ', prxi.bXInfoX);
  249.         PrintLine;
  250.     END;
  251.  
  252. { -------------------------------- Format TPrStl ----------------------------- }
  253.     PROCEDURE DumpPrStl(msg: Str255; ps: TPrStl);
  254.     BEGIN
  255.         PrintString(msg);
  256.         PrintStrHex('wDev: $', ps.wDev, 4);
  257.         DumpEnum('(', BSR(ps.wDev, 8), STRN_wdev); { device code }
  258.         PrintString(')');
  259.         PrintLine;
  260.  
  261.         PrintStrNum('iPageV: ', ps.iPageV);
  262.         PrintStrNum('iPageH: ', ps.iPageH);
  263.         PrintStrNum('bPort: ', ps.bPort);
  264.  
  265.         DumpEnum('feed: ', ORD(ps.feed), STRN_feed);
  266.  
  267.         PrintLine;
  268.     END;
  269.  
  270. { --------------------------------- Format TPrJob ---------------------------- }
  271.     PROCEDURE DumpPrJob(msg: Str255; pj: TPrJob);
  272.     BEGIN
  273.         PrintString(msg);
  274.         PrintStrNum('iFstPage: ', pj.iFstPage);
  275.         PrintStrNum('iLstPage: ', pj.iLstPage);
  276.         PrintStrNum('iCopies: ', pj.iCopies);
  277.  
  278.         DumpEnum('bJDocLoop: ', ORD(pj.bJDocLoop), STRN_job);
  279.         PrintLine;
  280.  
  281.         DumpEnum('fFromUsr: ', ORD(pj.fFromUsr), STRN_bool);
  282.  
  283.         PrintStrHex('pIdleProc: ', ORD4(pj.pIdleProc), 8);
  284.         PrintStrHex('pFileName ', ORD4(pj.pFileName), 8);
  285.         PrintLine;
  286.  
  287.         PrintStrNum('iFileVol: ', pj.iFileVol);
  288.         PrintStrNum('pFileVers: ', pj.bFileVers);
  289.         PrintStrNum('bJobX: ', pj.bJobX);
  290.         PrintLine;
  291.     END;
  292.  
  293. { --------------------------- Format printX Array ---------------------------- }
  294.     PROCEDURE DumpPrintX(msg: Str255; tpp: TPPrint);
  295.     VAR
  296.         i, max: INTEGER;
  297.     BEGIN { Outputs non-zero values, if any; ignore trailing run of zero}
  298.         max := 0;
  299.         FOR i := 1 TO 19 DO
  300.         IF (tpp^.printX[i] <> 0) THEN
  301.             max := i;         { ignore trailing zeroes }
  302.  
  303.         IF (max > 0) THEN
  304.         BEGIN
  305.         PrintString(msg);
  306.         FOR i := 1 TO max DO
  307.             BEGIN
  308.             PrintStrNum('[', i);
  309.             PrintString(']: ');
  310.             PrintHex(tpp^.printX[i], 4);
  311.             IF (((i MOD 4) = 0) OR (i = max)) THEN
  312.             PrintLine;     { every 4th or last one }
  313.             END
  314.         END
  315.     END;
  316.  
  317. { ------------------------------- Format TPrint ------------------------------ }
  318.     PROCEDURE DumpPrint(msg: Str255; hand: THPrint);
  319.     VAR
  320.         tpp: TPPrint;
  321.         i: INTEGER;
  322.     BEGIN
  323.         HLock(Handle(hand));
  324.         tpp := hand^; { pointer to a TPrint }
  325.  
  326.         PrintLine;
  327.         PrintString(msg);
  328.         PrintLine;
  329.         FOR i := 1 TO Length(msg) DO
  330.           PrintString('-');
  331.         PrintLine;
  332.  
  333.         PrintString('iPrVersion: ');
  334.         PrintInt(tpp^.iPrVersion);
  335.         PrintLine;
  336.  
  337.         DumpPrInfo('prInfo', tpp^.prInfo);
  338.         DumpRect('rPaper', tpp^.rPaper);
  339.         DumpPrStl('prStl', tpp^.prStl);
  340.         DumpPrInfo('prInfoPT', tpp^.prInfoPT);
  341.         DumpPrXInfo('prXInfo', tpp^.prXInfo);
  342.         DumpPrJob('prJob', tpp^.prJob);
  343.         DumpPrintX('printX', tpp);
  344.         PrintString(
  345. '------------------------------------------------------------------------------'
  346.             );
  347.         PrintLine;
  348.  
  349.         HUnLock(Handle(hand));
  350.     END; {DumpPrint}
  351.  
  352.  
  353. { =================================Windows Segment=============================}
  354. {$S Windows }
  355.  
  356. {-------------------------Update menus based on windows------------------------}
  357.   PROCEDURE CheckWindowMode;
  358.  
  359.     VAR
  360.       newmode: MyWindMode; 
  361.       fileset: SET OF 1..lastFileItem;
  362.       item: INTEGER;
  363.  
  364.     BEGIN    { This routine enable/disables menu items based on window mode }
  365.       myPeek := WindowPeek(FrontWindow);
  366.       IF myPeek = NIL THEN
  367.         newmode := NullMode        { no windows open }
  368.       ELSE
  369.         IF myPeek^.windowKind = MyDocument THEN
  370.       newmode := OpenMode        { document window on top }
  371.     ELSE
  372.       newmode := DAMode;        { D.A. on top }
  373.       IF newmode <> currWMode THEN    { Must change menus }
  374.         BEGIN
  375.       CASE newmode OF
  376.         NullMode:        { No windows open }
  377.           fileset := [newItem,quitItem];
  378.         OpenMode:        { One window open and on top }
  379.           fileset := [closeItem,stlItem,jobItem,setupItem,printItem,quitItem];
  380.         DAMode:        { DA on top }
  381.           fileset := [closeItem,quitItem];
  382.       END; {CASE}
  383.       FOR item := 1 TO lastFileItem DO
  384.         IF item IN fileset THEN
  385.           EnableItem(myMenus[fileMenu], item)
  386.         ELSE
  387.           DisableItem(myMenus[fileMenu], item);
  388.       IF newmode = DAMode THEN
  389.         EnableItem(myMenus[editMenu], 0)
  390.       ELSE
  391.         DisableItem(myMenus[editMenu], 0);
  392.       DrawMenuBar;        { menu dimming must be updated }
  393.       currWMode := newmode;
  394.     END; {IF}
  395.  
  396.     END; {CheckWindowMode}
  397.  
  398.  
  399. {---------------------------Close the front window-----------------------------}
  400.   PROCEDURE CloseAWindow;
  401.  
  402.     BEGIN
  403. { This routine closes an application (or DA) window, either after
  404.     • clicking go-away box
  405.     • selecting "Close" in File menu            }
  406.       myPeek := WindowPeek(FrontWindow);
  407.       IF myPeek^.windowKind = myDocument THEN
  408.       BEGIN
  409.         wdh := WindowDataHandle(GetWRefCon(WindowPtr(myPeek)));
  410.         ph := wdh^^.theTHP;
  411.         DisposHandle(Handle(ph));
  412.         TEDispose(hTE);
  413.         hTE := NIL;
  414.         DisposHandle(Handle(wdh));
  415.         DisposeWindow(myWindow);
  416.       END
  417.     ELSE { Must be a DA }
  418.       CloseDeskAcc(myPeek^.windowKind)
  419.      END; {CloseAWindow}
  420.  
  421.  
  422. {---------------------------Deactivate before dialog---------------------------}
  423.   PROCEDURE DialogueDeactivate;
  424.  
  425.     VAR
  426.       temprect: Rect;
  427.  
  428.     BEGIN
  429. {Deactivate the top window if we're about to put up a dialog}
  430.       SetCursor(arrow);
  431.       IF hTE <> NIL THEN {for documents, only}
  432.     TEDeactivate(hTE);
  433.     END; {DialogueDeactivate}
  434.  
  435.  
  436. {-------------------------Draw a document window-------------------------------}
  437.   PROCEDURE DrawWindow;
  438.  
  439.     VAR
  440.       tempport: GrafPtr;
  441.       temprect, rectToErase: Rect;
  442.       temppeek: WindowPeek;
  443.       whichwindow: WindowPtr;
  444.       temphTE: TEHandle;
  445.  
  446.     BEGIN
  447. {Draws the content region of the given window, after erasing whatever
  448.   was there before. }
  449.       whichwindow := WindowPtr(myEvent.message);
  450.       BeginUpdate(whichwindow);
  451.       GetPort(tempport);
  452.       SetPort(whichwindow);
  453.       temppeek := WindowPeek(whichwindow);
  454.       IF temppeek^.windowKind = myDocument THEN
  455.       BEGIN
  456.         temprect := whichwindow^.Portrect;
  457.         wdh := WindowDataHandle(GetWRefCon(whichwindow));
  458.         temphTE := wdh^^.theTE;
  459.         SetRect(temprect, - 32767, - 32767, 32767, 32767);
  460.         ClipRect(temprect);
  461. {this only erases the window past the end of text, if any}
  462.         WITH temphTE^^ DO
  463.           IF nLines < (viewRect.bottom - viewRect.top +
  464.          lineHeight) DIV lineHeight THEN
  465.         BEGIN
  466.           rectToErase := viewRect;
  467.           rectToErase.top := (nLines ) * lineHeight;
  468.           EraseRect(rectToErase)
  469.         END;
  470.         TEUpdate(whichwindow^.visRgn^^.rgnBBox, temphTE)
  471.       END;
  472.       SetPort(tempport);
  473.       EndUpdate(whichwindow)
  474.     END; {DrawWindow}
  475.  
  476.  
  477. {----------------------------Handle (de)activate events------------------------}
  478.   PROCEDURE MyActivate;
  479.  
  480.     BEGIN    {This activates or deactivates the current selection}
  481.       myWindow := WindowPtr(myEvent.message);
  482.       myPeek := WindowPeek(myWindow);
  483.       IF myPeek^.windowKind = myDocument THEN
  484.     BEGIN        { document window }
  485.       wdh := WindowDataHandle(GetWRefCon(myWindow));
  486.       hTE := wdh^^.theTE;
  487.       IF ODD(myEvent.modifiers) THEN { BAND(myEvent.modifiers,activeFlag)>0 }
  488.         TEActivate(hTE)    {this window is now top most}
  489.       ELSE            {this window is no longer top most}
  490.         BEGIN 
  491.           TEDeactivate(hTE);
  492.           hTE := NIL    {a TextEdit window is no longer on top}
  493.         END;
  494.     END;
  495.        
  496.     END; {MyActivate}
  497.  
  498. {------------------------------Create a new document window--------------------}
  499.   PROCEDURE OpenAWindow;
  500.  
  501.     VAR
  502.       r: Rect;
  503.  
  504.     BEGIN    {A window is created here}
  505.       IF spare <> NIL THEN
  506.     BEGIN
  507.       DisposPtr(spare);
  508.       spare := NIL
  509.     END;
  510.       myWindow := GetNewWindow(WIND_main, NIL, Pointer( - 1));
  511.       wdh := WindowDataHandle(NewHandle(SIZEOF(WindowData)));
  512.       SetWRefCon(myWindow, ORD(wdh));    { stash pointer to TEHandle in window }
  513.  
  514.       SetPort(myWindow);
  515.       myPeek := WindowPeek(myWindow);
  516.       TextFont(myStdFont);
  517.       TextSize(myStdSize);
  518.       DrawChar(' ');
  519.       SetFontLock(TRUE);
  520.       myPeek^.windowKind := myDocument; {identifies the type of window}
  521.  
  522.       r := myWindow^.Portrect;
  523.       InsetRect(r, 8, 4);
  524.       hTE := TENew(r, r);
  525.       wdh^^.theTE := hTE;
  526.       hTE^^.destRect := hTE^^.viewRect;
  527.       hTE^^.crOnly := -1;        { no automatic CR }
  528.  
  529.       PrOpen;
  530.       ph := THPrint(NewHandle(SIZEOF(TPrint)));
  531.       PrintDefault(ph);
  532.       wdh^^.theTHP := ph;
  533.       DumpPrint('After PrintDefault(…)', ph);
  534.       PrClose;
  535.      
  536.     END; {OpenAWindow}
  537.  
  538.  
  539. { ================================Printing Segment=============================}
  540. {$S Printing }
  541.  
  542. {----------------------------Print out a document window-----------------------}
  543.   PROCEDURE DoPrinting;
  544.  
  545.     CONST
  546.       bottommargin = 20;{ margins (in pixels) for document, inset from rPage }
  547.       leftmargin = 30;
  548.       rightmargin = 10;
  549.       topmargin = 36;
  550.  
  551.     VAR
  552.       txth: Handle;
  553.       printTE: TEHandle;
  554.       MyPPort: TPPrPort;
  555.       dlogptr: DialogPtr;
  556.       txtptr: Ptr;
  557.       linesperpage, height, firstoffset, lastoffset, leftpos, toppos, fstpos,
  558.     lineno, lastline, linecount, pageno, firstpage, lastpage, numpages, 
  559.     copyno, numpasses, dummyitem, errno: INTEGER;
  560.       pagerect: Rect;
  561.       currstr, laststr, heading: Str255;
  562.       strh0, strh1, hdgstrh: StringHandle;
  563.       status: TPrStatus;
  564.       info: FontInfo;
  565.       lastonpage: ARRAY [0..99] OF INTEGER;    { last line # on each page }
  566.  
  567.     BEGIN
  568. {  This section images each page, using QuickDraw via TextEdit
  569.    A few special cases:
  570.      1. For spooled output (IW only), must image and then print
  571.     2. For IW draft mode, must send multiple copies ourself
  572.    This has been completely rewritten from skeleton code, for a number
  573.    of key reasons:
  574.        1. A location is calculated for each line and then DrawText is
  575.        used to draw the line.  This also requires setting the font
  576.        directly in the printing GrafPort.  The skeleton used TextBox
  577.        for each page; TextBox uses an EraseRect which, according to
  578.        Technical Note #72, is very slow on the LaserWriter.
  579.     2. We use crOnly, so only actual returns are used for line
  580.        breaks.  Thus, we don't need a new TECalText for the
  581.        printing destRect, but instead use the TextEdit lineStarts
  582.        established for display purposes.
  583.     3. This routine figures out what the actual pages selected are,
  584.        and then prints only those pages.  (The values of prJob.iFstPage
  585.        and iLstPage need to be fudged to do this.)
  586.     4. Put a heading on each page, showing page number.
  587.     5. Put up an Alert if a printing error is encountered.  Not strictly
  588.        necessary, since the most commonly found "errors" are user-
  589.        specified aborts that should be ignored.
  590. }
  591.  
  592.       printFlag := FALSE;    { so we don't print again }
  593.       DialogueDeactivate;
  594.       IF PrJobDialog(printHdl) THEN
  595.     BEGIN
  596.       SetCursor(watchHdl^^);
  597. { Put up progress dialog }
  598.       strh0 := GetString(STR_prepare);
  599.       ParamText(strh0^^,'','','');
  600.       dlogptr := GetNewDialog(DLOG_printing, NIL, Pointer(-1));
  601.       DrawDialog(dlogptr);
  602.       printTE := hTE;
  603.  
  604. { Calculate number of pages, and line numbers for each page }
  605.       WITH printTE^^, printHdl^^.PrInfo DO
  606.         BEGIN
  607.           txth := hText;
  608.           height := lineHeight;
  609.           linecount := nLines;
  610.           linesperpage := (rPage.bottom - rPage.top - bottommargin - topmargin)
  611.                 DIV height;
  612.           pagerect := rPage;    { top margin allows for heading }
  613.           pagerect.left := pagerect.left + leftmargin;
  614.           pagerect.right := pagerect.right - rightmargin;
  615.           pagerect.bottom := pagerect.top + topmargin + 
  616.                       (linesperpage * height);
  617.           fstpos := pagerect.top + topmargin + fontAscent;
  618.               { base line of first line of text in document }
  619.         END; {WITH}
  620.       lastonpage[0] := 0;
  621.       pageno := 1;
  622.       lineno := 0;
  623.       WHILE lineno < linecount DO    { until we run out of pages }
  624.         BEGIN
  625.           lineno := lineno + linesperpage;
  626.           IF lineno < linecount THEN    { all but last page }
  627.         lastonpage[pageno] := lineno-1    { last line on this page }
  628.           ELSE                { last page }
  629.         lastonpage[pageno] := linecount-1;{ lines numbered 0..n }
  630.           pageno := pageno + 1;
  631.         END; {WHILE}
  632.       numpages := pageno - 1;
  633.  
  634. { We could skip page calculations, but then we would image all pages and
  635.   Print Manager would print only those selected.  Obviously this is
  636.   inefficient for large documents.  Instead, fool Print Manager into
  637.   thinking enough pages are selected and then do actual printing starting
  638.   at the selected page.  This MUST be done before PrOpenDoc.        }
  639.       WITH printHdl^^.PrJob DO
  640.         BEGIN
  641.           firstpage := iFstPage;    { page numbers requested }
  642.           IF firstpage < 1 THEN
  643.         firstpage := 1;
  644.           lastpage := iLstPage;
  645.           IF lastpage > numpages THEN
  646.         lastpage := numpages;    { limit to available pages }
  647.           numpages := lastpage - firstpage + 1; { actual length }
  648.           iFstPage := 1;        { fool print manager }
  649.           iLstPage := numpages;    { reset by next PrJobDialog }
  650. { Manual handling of multiple copies for draft mode only
  651.   ImageWriter spooling handles this directly; the LaserWriter PrJobDialog
  652.   always sets iCopies := 1 and hides the actual number of copies from us
  653.   Also set up appropriate progress message                 }
  654.           IF bJDocLoop = bSpoolLoop THEN
  655.             BEGIN
  656.           numpasses := 1;        { only one pass through }
  657.           strh0 := GetString(STR_spooling);{ "Now spooling " }
  658.         END
  659.           ELSE
  660.             BEGIN
  661.           numpasses := iCopies;        { draft mode, multiple passes }
  662.           strh0 := GetString(STR_printing);{ "Now printing " }
  663.         END;
  664.         END; {WITH}
  665.       strh1 := GetString(STR_of);        { " of " }
  666.       hdgstrh := GetString(STR_pagehead);    { "Page " }
  667.  
  668. { Now do actual printing (or imaging, for spool mode
  669.   Get a drawing port: TPrint should be frozen by now
  670.   Go through it once for every copy (if necessary) and once per page 
  671.   Show dialog progress in terms of pages to be printed            }
  672.       MyPPort := PrOpenDoc(printHdl, NIL, NIL);
  673.       NumToString(numpages, laststr);    { actual # of pages to print }
  674.       FOR copyno := 1 TO numpasses DO
  675.         BEGIN
  676.           MoveHHi(txth);
  677.           HLock(txth);
  678.           txtptr := txth^;
  679.           FOR pageno := firstpage TO lastpage DO
  680.         BEGIN    { Image each page; this does printing for draft mode }
  681.           IF PrError <> noErr THEN
  682.             LEAVE;        { quit }
  683.           NumToString(pageno-firstpage+1, currstr); {relative page #}
  684.           ParamText(strh0^^, currstr, strh1^^, laststr);
  685.           DrawDialog(dlogptr);        { update the status }
  686.  
  687.           PrOpenPage(MyPPort, NIL);    { changes GrafPort for us }
  688. { First put a heading on the page.  Since MoveTo location for drawing text
  689.   is the base line, need ascent to position heading within pagerect }
  690.           TextFont(myHdgFont);
  691.           TextSize(myHdgSize);
  692.           GetFontInfo(info);        { need ascent height }
  693.           NumToString(pageno, heading);    { absolute page number }
  694.             heading := Concat(hdgstrh^^, heading); { "Page 1" }
  695.           WITH pagerect DO
  696.             BEGIN
  697.               leftpos := left +        { center }
  698.                 ((right-left - StringWidth(heading)) DIV 2 );
  699.               MoveTo(leftpos, top+info.ascent); { base line }
  700.               DrawString(heading);    { print page heading }
  701. { Now print actual document for this page }
  702.               leftpos := left;        { left margin for text }
  703.               toppos := fstpos;        { base line for 1st line }
  704.             END;
  705.           TextFont(printTE^^.txFont);    { set for display }
  706.           TextSize(printTE^^.txSize);
  707.           lineno := lastonpage[pageno-1];{ line of TERec }
  708.           firstoffset := printTE^^.lineStarts[lineno];
  709.           lastline := lastonpage[pageno];
  710. { Draw each line in TERec, excluding CR at end of line }
  711.           WHILE lineno <= lastline DO
  712.             BEGIN
  713.               MoveTo(leftpos, toppos);
  714.               lineno := lineno + 1;
  715.               IF lineno >= linecount THEN
  716.                 lastoffset := printTE^^.teLength  { very last line }
  717.               ELSE
  718.                 lastoffset := printTE^^.lineStarts[lineno]-1;
  719.               DrawText(txtptr, firstoffset, lastoffset-firstoffset);
  720.               toppos := toppos + height;
  721.               firstoffset := lastoffset+1;
  722.             END; {each line}
  723.           PrClosePage(MyPPort);        { done with this page }
  724.         END; {each page}
  725.           HUnLock(txth);
  726.         END; {each copy}
  727.       PrCloseDoc(MyPPort);
  728. { If spooled, the file is now imaged and now need to print it }
  729.       IF (printHdl^^.prJob.BJDocLoop = BSpoolLoop)
  730.             AND (PrError = noErr) THEN
  731.         BEGIN
  732.           strh0 := GetString(STR_prspool);    { "Now printing spool file" }
  733.           ParamText(strh0^^, '', '', '');
  734.           DrawDialog(dlogptr);
  735.           PrPicFile(printHdl, NIL, NIL, NIL, status);
  736.         END;
  737. { Drop the advice dialog }
  738.       DisposDialog(dlogptr);
  739.       SetCursor(arrow);
  740.       errno := PrError;
  741.       IF (errno <> noErr) AND  { indicate a printing error, unless… }
  742.          (errno <> iPrAbort) AND    { user hit command-period }
  743.          (errno <> iIOAbort) THEN    { user cancel on "not responding" alert }
  744.         BEGIN
  745.           NumToString(errno, currstr);    { error number }
  746.           ParamText(currstr, '', '', '');    { should be more user-friendly }
  747.           dummyitem := StopAlert(ALRT_printerr, NIL);
  748.         END;
  749.     END {IF PrJobDialog}
  750.       ELSE { Cancel in PrJobDlog }
  751.     PrSetError(iPrAbort);
  752.     END; {DoPrinting}
  753.  
  754.  
  755. { ===============================Initialization Segment========================}
  756. {$S Initial }
  757.  
  758. {---------------------------------Initialize everything------------------------}
  759.   PROCEDURE SetUp;
  760.  
  761.     VAR
  762.       counter: INTEGER;
  763.  
  764.     BEGIN
  765. {    Come here with InitGraf and InitWindows already done to avoid fragmentation
  766.     Put this code in a segment that can be immediately unloaded}
  767.  
  768.       InitFonts;            { the last 5 of the standard 7 }
  769.       FlushEvents(everyEvent, 0);
  770.       TEInit;
  771.       InitDialogs(NIL);
  772.       InitMenus; {initialize Menu Manager }
  773.  
  774.       watchHdl := GetCursor(WatchCursor);
  775.       HNoPurge(Handle(watchHdl));
  776.  
  777.       printHdl := THPrint(NewHandle(SizeOf(TPrint)));
  778.       PrOpen;
  779.       PrintDefault(printHdl);    { the one used for actual printing }
  780.       PrClose;
  781.  
  782. { Build some menus }
  783.       FOR counter := 1 TO lastMenu DO
  784.     myMenus[counter] := GetMenu(counter);
  785.       AddResMenu(myMenus[1], 'DRVR'); {desk accessories }
  786.       FOR counter := 1 TO lastMenu DO
  787.     InsertMenu(myMenus[counter], 0);
  788.       DrawMenuBar;
  789.  
  790.       dragRect := screenbits.bounds;
  791.       dragRect.top := dragRect.top + 20; {leave room for menu bar}
  792.       InsetRect(dragRect, 4, 4); {leave some of dragged rectangle on screen}
  793.       doneFlag := FALSE;
  794.       printFlag := FALSE;
  795.  
  796.       currWMode := nullMode;
  797.       OpenAWindow;        { put up the window for displaying output }
  798.     END; {SetUp}
  799.  
  800.  
  801. { ==============================Menu commands Segment==========================}
  802. {$S Command}
  803.  
  804. {----------------------------------Alert for About…----------------------------}
  805.   PROCEDURE AboutThisProgram;
  806.  
  807.     VAR
  808.       itemhit: INTEGER;
  809.       hand: StringHandle;
  810.  
  811.     BEGIN
  812.       DialogueDeactivate;
  813.       hand := GetString(STR_id);        { identity string }
  814.       ParamText(hand^^, '', '', '');
  815.       itemhit := NoteAlert(ALRT_about, NIL);
  816.     END; {AboutThisProgram}
  817.  
  818.  
  819. {---------------------------------Handle menu command--------------------------}
  820. PROCEDURE DoCommand (commandkey: BOOLEAN);
  821.   VAR 
  822.     daname: Str255;
  823.     refnum, theMenu, theItem: INTEGER;
  824.     menuResult: LONGINT;
  825.     daedit: BOOLEAN;
  826. BEGIN
  827.     IF commandkey THEN
  828.       menuResult := MenuKey(theChar)
  829.     ELSE
  830.       menuResult := MenuSelect(myEvent.where);
  831.     theMenu := HiWrd(menuResult);        { HiWord() }
  832.     theItem := LoWrd(menuResult);        { LoWord() }
  833.     CASE theMenu OF
  834.       appleMenu:  {enough memory to allow desk accessory to open}
  835.         BEGIN
  836.       IF theItem = 1 THEN
  837.         AboutThisProgram
  838.       ELSE
  839.         BEGIN
  840.           GetItem(myMenus[appleMenu],theItem,daname);
  841.           DisposPtr(spare);
  842.           spare := NIL;
  843.           refNum := OpenDeskAcc(daname)
  844.         END
  845.         END;
  846.       fileMenu:
  847.     BEGIN
  848.       CASE theItem OF
  849.         newItem:            {New }
  850.           OpenAWindow;
  851.         closeItem:                  {Close }
  852.           CloseAWindow;
  853.         stlItem:            {PrStlDialog… }
  854.           BEGIN
  855.          PrOpen;
  856.          DialogueDeactivate;
  857.          IF PrStlDialog (wdh^^.theTHP) THEN
  858.            DumpPrint('After PrintStlDialog(…)', wdh^^.theTHP);
  859.          PrClose
  860.            END;
  861.         jobItem:            {PrJobDialog… }
  862.           BEGIN    { just modifying TPrint, not actually printing }
  863.          PrOpen;
  864.          DialogueDeactivate;
  865.          IF PrJobDialog (wdh^^.theTHP) THEN
  866.            DumpPrint('After PrJobDialog(…)', wdh^^.theTHP);
  867.          PrClose
  868.            END;
  869.         setupItem:            {Page Setup… }
  870.            BEGIN
  871.           PrOpen;
  872.           DialogueDeactivate;
  873.           IF PrStlDialog (PrintHdl)
  874.           THEN ;
  875.           PrClose
  876.         END;
  877.         printItem:            {Print }
  878.           printFlag := TRUE;
  879. { Call actual printing logic from main event loop to unload maximal segments }
  880.         quitItem:            {Quit }
  881.           doneFlag := TRUE;
  882.       END {CASE theItem}
  883.     END; {fileMenu}
  884.       editMenu:
  885.     daedit := SystemEdit(theitem-1);
  886.   END; {CASE}
  887.  
  888.   HiLiteMenu(0);
  889. END;  {DoCommand}
  890.  
  891.  
  892. { =========================Main Segment (never unloaded)=======================}
  893. {$S Main}
  894.  
  895. {-------------------------------The main event loop----------------------------}
  896.   PROCEDURE MainEventLoop;
  897.  
  898.     VAR
  899.       tempwindow: WindowPtr; {window referenced by GetNextEvent}
  900.  
  901.     BEGIN
  902.       REPEAT
  903.     SystemTask;
  904.     IF printFlag THEN
  905.       BEGIN
  906.         PrOpen;
  907.         DoPrinting;
  908.         PrClose
  909.       END;
  910.     IF GetNextEvent(everyEvent, myEvent) THEN
  911.       BEGIN
  912.         CASE myEvent.what OF
  913.           mouseDown:
  914.         BEGIN
  915.           CASE FindWindow(myEvent.where, tempwindow) OF
  916.             inMenuBar:
  917.               DoCommand(FALSE);
  918.             inSysWindow:
  919.               SystemClick(myEvent, tempwindow);
  920.             inDrag:
  921.               DragWindow(tempwindow, myEvent.where, dragRect);
  922.             inContent:
  923.               SysBeep(1);
  924.             inGoAway:
  925.               IF TrackGoAway(tempwindow, myEvent.where) THEN
  926.             CloseAWindow;
  927.           END {of code case }
  928.         END; {of mouseDown }
  929.           keyDown, autoKey:
  930.         BEGIN    { replace BAND() with BitAnd() if necessary }
  931.           theChar := CHR(BAND(myEvent.message, charCodeMask));
  932.           IF BAND(myEvent.modifiers, CmdKey) <> 0 THEN
  933.             DoCommand(TRUE)    { do menu equivalent }
  934.           ELSE
  935.             SysBeep(1);        { no typing allowed! }
  936.         END; {of keyDown}
  937.           activateEvt:
  938.         MyActivate;
  939.           updateEvt:
  940.         DrawWindow;
  941.         END; {of event case }
  942.       CheckWindowMode;
  943.       END
  944.     ELSE
  945.       IF (myEvent.what = nullEvent) AND doneFlag AND 
  946.          (FrontWindow <> NIL) THEN
  947.         CloseAWindow;
  948. { We like to leave lots of memory available, so unload everything as a precaution }
  949.     UnloadSeg(@DoCommand);        {segment DoCommand}
  950.     UnloadSeg(@PrintLine);        {segment DumpTPrint}
  951.     UnloadSeg(@OpenAWindow);    {segment Windows}
  952.      UnloadSeg(@DoPrinting);        {segment Printing}
  953.     IF spare = NIL THEN {Create a space for the next window to be opened.}
  954.       spare := NewPtr(SizeOf(dialogRecord));
  955. {Since all segments are unloaded, this will not fragment the free memory space.}
  956.       UNTIL doneFlag AND (FrontWindow = NIL);
  957.     END;
  958.  
  959. {-----------------------------Memory initialization----------------------------}
  960.   PROCEDURE SetUpMemory;    { Initialize in main segment }
  961.     BEGIN
  962.       MaxApplZone;
  963.       MoreMasters;
  964.       MoreMasters;
  965.       MoreMasters;
  966.       MoreMasters;
  967.     END;
  968.  
  969. {-------------------------------Main program-----------------------------------}
  970.   BEGIN {main program }
  971.     SetUpMemory;
  972.     InitGraf(@thePort);
  973.     InitWindows;    { allocates a nonrelocatable block}
  974.     spare := NewPtr(SizeOf(dialogRecord));
  975. { reserve space for one window, since this is non-relocatable }
  976.     SetUp;        { do normal setup }
  977.     UnloadSeg(@SetUp);    { done with it forever }
  978.  
  979.     InitCursor;
  980.     MainEventLoop;
  981.     SetCursor(watchHdl^^);
  982.   END.  {main}
  983.